/*
 * Reksio - Memory Map Editor
 * Copyright (C) 2023 CERN
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 *
 * In applying this licence, CERN does not waive the privileges and immunities
 * granted to it by virtue of its status as an Intergovernmental Organization or
 * submit itself to any jurisdiction.
 */

#ifndef COMMANDS_H
#define COMMANDS_H
#include <QUndoCommand>
#include <QList>
#include <initializer_list>
#include "attributesmodel.h"
#include "nodesmodel.h"

class NodeChangedBaseCommand: public QUndoCommand
{
public:
    explicit NodeChangedBaseCommand(const QModelIndex& index);
    explicit NodeChangedBaseCommand(const QPersistentModelIndex& index);
    explicit NodeChangedBaseCommand(std::initializer_list<QPersistentModelIndex> l);
    virtual ~NodeChangedBaseCommand() {};
    const QList<QPersistentModelIndex> node_index_list;
};

class DummyCommand : public NodeChangedBaseCommand
{
public:
    explicit DummyCommand(const QModelIndex& index, const QString& text);
    void redo() override {}
    void undo() override {}
};

class ChangeAttributeCommand : public NodeChangedBaseCommand
{
public:
    explicit ChangeAttributeCommand(const QModelIndex& index, const QModelIndex& nodes_model_parent, const QString& val);
    void redo() override;
    void undo() override;
private:
    void setAttribute(const QString & value);
    AttributesModel* attributes_model;
    Attribute* attr;
    NodesModel* nodes_model;
    QString new_val;
    QString old_val;
};

class AddChildCommand : public NodeChangedBaseCommand
{
public:
    explicit AddChildCommand(const int& position, const QString& type, const QModelIndex& nodes_model_parent);
    void redo() override;
    void undo() override;
private:
    const int position;
    const QString type;
    NodesModel* model;
    QPersistentModelIndex parent_index;
    std::unique_ptr<MemoryNode> node_copy;
    MemoryNode* parent_node;
};

class AddChildrenCommand : public NodeChangedBaseCommand
{
public:
    explicit AddChildrenCommand(const std::vector<std::string>& types, const QModelIndex& nodes_model_parent);
    void redo() override;
    void undo() override;
private:
    const std::vector<std::string> types;
    NodesModel* model;
    QPersistentModelIndex parent_index;
    std::unique_ptr<MemoryNode> node_copy;
    MemoryNode* parent_node;
};

class MoveCommand : public NodeChangedBaseCommand
{
public:
    enum MoveDirection
    {
        up = -1,
        down = 2
    };
    explicit MoveCommand(MoveDirection direction, QPersistentModelIndex index, int count);
    void redo() override;
    void undo() override;
    void move(MoveDirection direction);
    static MoveDirection reverse_dir(MoveDirection dir);
private:
    MoveDirection dir;
    QPersistentModelIndex index;
    int count;
    NodesModel * nodes_model;
};

class RemoveNodeCommand: public NodeChangedBaseCommand
{
public:
    explicit RemoveNodeCommand(QPersistentModelIndex index);
    void redo() override;
    void undo() override;
private:
    QPersistentModelIndex parent;
    int row;
    NodesModel * nodes_model;
    std::unique_ptr<MemoryNode> node_copy;
};

class DuplicateNodeCommand: public NodeChangedBaseCommand
{
public:
    explicit DuplicateNodeCommand(QPersistentModelIndex index, int targetPosition, QPersistentModelIndex target_index);
    void redo() override;
    void undo() override;
private:
    int row;
    QPersistentModelIndex index;
    QPersistentModelIndex target_index;
    NodesModel* nodes_model;
    std::unique_ptr<MemoryNode> node_copy;
};

class AddAttributeContainerCommand: public NodeChangedBaseCommand
{
public:
    explicit AddAttributeContainerCommand(const int& position, const QString name, const QModelIndex& node_index, const QModelIndex& container_index, AttributesModel* model);
    void redo() override;
    void undo() override;
private:
    const int position;
    const QString name;
    AttributesModel* attributes_model;
    QPersistentModelIndex parent_container_index;
    bool hasValidParent;
    std::vector<std::string> parent_container_name;
    std::unique_ptr<AttributeContainer> attribute_container_copy;
};

class AddAttributeCommand: public NodeChangedBaseCommand
{
public:
    explicit AddAttributeCommand(const QString name, const QModelIndex& node_index, const QModelIndex& container_index, AttributesModel* model, const QString value = "");
    void redo() override;
    void undo() override;
private:
    const QString name;
    const QString value;
    AttributesModel* attributes_model;
    const std::vector<std::string> container_name;
    QPersistentModelIndex parent_container_index;
    bool hasValidParent;
    std::unique_ptr<Attribute> attribute_copy;
};

class RemoveAttributeCommand: public NodeChangedBaseCommand
{
public:
    explicit RemoveAttributeCommand(const QModelIndex& index, const QModelIndex& tree_node_parent);
    void redo() override;
    void undo() override;
private:
    QString name;
    AttributesModel* attributes_model;
    bool hasValidParent;
    const std::vector<std::string> container_name;
    QPersistentModelIndex parent_container_index;
    std::unique_ptr<Attribute> attribute_copy;
};

class RemoveAttributeContainerCommand: public NodeChangedBaseCommand
{
public:
    explicit RemoveAttributeContainerCommand(const QModelIndex& index, const QModelIndex& tree_node_parent);
    void redo() override;
    void undo() override;
private:
    QString name;
    AttributesModel* attributes_model;
    bool hasValidParent;
    QPersistentModelIndex parent_container_index;
    std::vector<std::string> parent_container_name;
    std::unique_ptr<AttributeContainer> attribute_container_copy;
};

class DragNodeCommand: public NodeChangedBaseCommand
{
public:
    explicit DragNodeCommand(const QModelIndex &sourceParent, int sourcePosition, int sourceCount, const QModelIndex &destinationParent, int destPosition);
    void redo() override;
    void undo() override;
private:
    QPersistentModelIndex sourceParent;
    int sourcePosition;
    int sourceCount;
    QPersistentModelIndex destinationParent;
    int destPosition;
    NodesModel* nodes_model;
};

#endif // COMMANDS_H
